package com.zjw.shortlink.project.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zjw.shortlink.project.common.convention.exception.ServiceException;
import com.zjw.shortlink.project.common.user.UserContext;
import com.zjw.shortlink.project.dao.entity.*;
import com.zjw.shortlink.project.dao.mapper.*;
import com.zjw.shortlink.project.dto.req.ShortLinkGroupStatsAccessRecordReqDTO;
import com.zjw.shortlink.project.dto.req.ShortLinkGroupStatsReqDTO;
import com.zjw.shortlink.project.dto.req.ShortLinkStatsAccessRecordReqDTO;
import com.zjw.shortlink.project.dto.req.ShortLinkStatsReqDTO;
import com.zjw.shortlink.project.dto.resp.*;
import com.zjw.shortlink.project.service.ShortLinkStatsService;
import lombok.RequiredArgsConstructor;
import org.redisson.misc.Hash;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
public class ShortLinkStatsServiceImpl implements ShortLinkStatsService {
    private final LinkGroupMapper linkGroupMapper;
    private final LinkAccessStatsMapper linkAccessStatsMapper;
    private final LinkLocaleStatsMapper linkLocaleStatsMapper;
    private final LinkNetworkStatsMapper linkNetworkStatsMapper;
    private final LinkAccessLogsMapper linkAccessLogsMapper;
    private final LinkDeviceStatsMapper linkDeviceStatsMapper;
    private final LinkOsStatsMapper linkOsStatsMapper;
    private final LinkBrowserStatsMapper linkBrowserStatsMapper;

    @Override
    public ShortLinkStatsRespDTO oneShortLinkStats(ShortLinkStatsReqDTO requestParams) {
        checkGroupBelongToUser(requestParams.getGid());
        // 在限定时间段内，每天的pv、uv、uip访问量
        List<LinkAccessStatsDO> linkAccessStatsDOS = linkAccessStatsMapper.listStatsByShortLink(requestParams);
        if(CollUtil.isEmpty(linkAccessStatsDOS)) {
            return null;
        }
        // 在限定时间段内，全部的pv、uv、uip访问量
        LinkAccessStatsDO pvUvUidStatsByShortLink = linkAccessLogsMapper.findPvUvUidStatsByShortLink(requestParams);
        // 统计日常pv、uv、uip访问量
        List<ShortLinkStatsAccessDailyRespDTO> daily = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(requestParams.getStartDate()), DateUtil.parse(requestParams.getEndDate()), DateField.DAY_OF_MONTH).stream()
                .map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> {
            linkAccessStatsDOS.stream()
                    .filter(item -> Objects.equals(DateUtil.formatDate(item.getDate()), each))
                    .findFirst()
                    .ifPresentOrElse(item -> {
                        ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                                .pv(item.getPv())
                                .uv(item.getUv())
                                .uip(item.getUip())
                                .date(each)
                                .build();
                        daily.add(accessDailyRespDTO);
                    }, () -> {
                        ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                                .pv(0)
                                .uv(0)
                                .uip(0)
                                .date(each)
                                .build();
                        daily.add(accessDailyRespDTO);
                    });
        });
        // 统计地区访问详情
        List<ShortLinkStatsLocaleCNRespDTO> localeCnStats = new ArrayList<>();
        List<LinkLocaleStatsDO> linkLocaleStatsDOS = linkLocaleStatsMapper.listLocaleByShortLink(requestParams);
        int localeCnSum = linkLocaleStatsDOS.stream().mapToInt(LinkLocaleStatsDO::getCnt).sum();
        linkLocaleStatsDOS.forEach(item -> {
            double radio = (double) item.getCnt() / localeCnSum;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .locale(item.getProvince())
                    .cnt(item.getCnt())
                    .ratio(actualRadio)
                    .build();
            localeCnStats.add(localeCNRespDTO);
        });
        // 统计24h访问情况
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDO> listHourStatsByShortLink = linkAccessStatsMapper.listHourStatsByShortLink(requestParams);
        for(int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
            int hourPv = listHourStatsByShortLink.stream()
                    .filter(item -> Objects.equals(item.getHour(), hour.get()))
                    .findFirst()
                    .map(LinkAccessStatsDO::getPv)
                    .orElse(0);
            hourStats.add(hourPv);
        }
        // 高频ip访问详情
        List<ShortLinkStatsTopIpRespDTO> topIpStats = new ArrayList();
        List<HashMap<String, Object>> listTopIpByShortLink = linkAccessLogsMapper.listTopIpByShortLink(requestParams);
        listTopIpByShortLink.forEach(each -> {
            ShortLinkStatsTopIpRespDTO statsTopIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpStats.add(statsTopIpRespDTO);
        });
        // 一周访问详情
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStatsDO> listWeekdayStatsByShortLink = linkAccessStatsMapper.listWeekdayStatsByShortLink(requestParams);
        for(int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByShortLink.stream()
                    .filter(item -> Objects.equals(item.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStatsDO::getPv)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        // 浏览器访问详情
        List<ShortLinkStatsBrowserRespDTO> browserStats = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByShortLink = linkBrowserStatsMapper.listBrowserStatsByShortLink(requestParams);
        int broswerCnt = listBrowserStatsByShortLink.stream()
                .mapToInt(item -> Integer.parseInt(item.get("count").toString()))
                .sum();
        listBrowserStatsByShortLink.forEach(item -> {
            int cnt = Integer.parseInt(item.get("count").toString());
            double radio = (double) cnt / broswerCnt;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsBrowserRespDTO browser = ShortLinkStatsBrowserRespDTO.builder()
                    .browser(item.get("browser").toString())
                    .cnt(cnt)
                    .ratio(actualRadio)
                    .build();
            browserStats.add(browser);
        });
        // 操作系统访问详情
        List<ShortLinkStatsOsRespDTO> osStats = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByShortLink = linkOsStatsMapper.listOsStatsByShortLink(requestParams);
        int osCnt = listOsStatsByShortLink.stream()
                .mapToInt(item -> Integer.parseInt(item.get("count").toString()))
                .sum();
        listOsStatsByShortLink.forEach(each -> {
            int cnt = Integer.parseInt(each.get("count").toString());
            double radio = (double) cnt / osCnt;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsOsRespDTO os = ShortLinkStatsOsRespDTO.builder()
                    .os(each.get("os").toString())
                    .cnt(cnt)
                    .ratio(actualRadio)
                    .build();
            osStats.add(os);
        });
        // 访客类型访问详情
        List<ShortLinkStatsUvRespDTO> uvTypeStats = new ArrayList<>();
        HashMap<String, Object> uvTypeStatsByShortLink = linkAccessLogsMapper.findUvTypeCntByShortLink(requestParams);
        int oldUserCnt = Integer.parseInt(Optional.ofNullable(uvTypeStatsByShortLink)
                .map(item -> item.get("oldUserCnt"))
                .map(Object::toString)
                .orElse("0"));
        int newUserCnt = Integer.parseInt(Optional.ofNullable(uvTypeStatsByShortLink)
                .map(item -> item.get("newUserCnt"))
                .map(Object::toString)
                .orElse("0"));
        int userCnt = oldUserCnt + newUserCnt;
        double oldUserRadio = (double) oldUserCnt / userCnt;
        double actualOldUserRadio = Math.round(oldUserRadio * 100.0) / 100.0;
        double newUserRadio = (double) newUserCnt / userCnt;
        double actualNewUserRadio = Math.round(newUserRadio * 100.0) / 100.0;
        ShortLinkStatsUvRespDTO oldUserStats = ShortLinkStatsUvRespDTO.builder()
                .uvType("oldUser")
                .cnt(oldUserCnt)
                .ratio(actualOldUserRadio)
                .build();
        uvTypeStats.add(oldUserStats);
        ShortLinkStatsUvRespDTO newUserStats = ShortLinkStatsUvRespDTO.builder()
                .uvType("newUser")
                .cnt(newUserCnt)
                .ratio(actualNewUserRadio)
                .build();
        uvTypeStats.add(newUserStats);
        // 统计设备访问详情
        List<ShortLinkStatsDeviceRespDTO> deviceStats = new ArrayList<>();
        List<LinkDeviceStatsDO> linkDeviceStatsDOS = linkDeviceStatsMapper.listDeviceStatsByShortLink(requestParams);
        int deviceCnt = linkDeviceStatsDOS.stream()
                .mapToInt(LinkDeviceStatsDO::getCnt)
                .sum();
        linkDeviceStatsDOS.forEach(item -> {
            double radio = (double) item.getCnt() / deviceCnt;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsDeviceRespDTO deviceRespDTO = ShortLinkStatsDeviceRespDTO.builder()
                    .device(item.getDevice())
                    .cnt(item.getCnt())
                    .ratio(actualRadio)
                    .build();
            deviceStats.add(deviceRespDTO);
        });
        // 统计网络访问详情
        List<ShortLinkStatsNetworkRespDTO> networkStats = new ArrayList<>();
        List<LinkNetworkStatsDO> linkNetworkStatsDOS = linkNetworkStatsMapper.listNetworkStatsByShortLink(requestParams);
        int networkCnt = linkNetworkStatsDOS.stream()
                .mapToInt(LinkNetworkStatsDO::getCnt)
                .sum();
        linkNetworkStatsDOS.forEach(each -> {
            double radio = (double) each.getCnt() / networkCnt;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsNetworkRespDTO networkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .network(each.getNetwork())
                    .cnt(each.getCnt())
                    .ratio(actualRadio)
                    .build();
            networkStats.add(networkRespDTO);
        });
        return ShortLinkStatsRespDTO.builder()
                .pv(pvUvUidStatsByShortLink.getPv())
                .uv(pvUvUidStatsByShortLink.getUv())
                .uip(pvUvUidStatsByShortLink.getUip())
                .daily(daily)
                .localeCnStats(localeCnStats)
                .hourStats(hourStats)
                .topIpStats(topIpStats)
                .weekdayStats(weekdayStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .uvTypeStats(uvTypeStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();
    }

    @Override
    public IPage<ShortLinkStatsAccessRecordRespDTO> shortLinkStatsAccessRecord(ShortLinkStatsAccessRecordReqDTO requestParam) {
        checkGroupBelongToUser(requestParam.getGid());
        LambdaQueryWrapper<LinkAccessLogsDO> queryWrapper = new LambdaQueryWrapper<LinkAccessLogsDO>()
                .eq(LinkAccessLogsDO::getFullShortUrl, requestParam.getFullShortUrl())
                .between(LinkAccessLogsDO::getCreateTime, requestParam.getStartDate(), requestParam.getEndDate())
                .eq(LinkAccessLogsDO::getDelFlag, 0)
                .orderByDesc(LinkAccessLogsDO::getCreateTime);
        IPage<LinkAccessLogsDO> linkAccessLogsDOIPage = linkAccessLogsMapper.selectPage(requestParam, queryWrapper);
        IPage<ShortLinkStatsAccessRecordRespDTO> actualResult = linkAccessLogsDOIPage.convert(each -> BeanUtil.toBean(each, ShortLinkStatsAccessRecordRespDTO.class));
        if(CollUtil.isEmpty(actualResult.getRecords())) {
            return new Page<>();
        }
        List<String> userList = actualResult.getRecords().stream().map(ShortLinkStatsAccessRecordRespDTO::getUser).toList();
        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectUvTypeByUsers(
                requestParam.getGid(),
                requestParam.getFullShortUrl(),
                requestParam.getEnableStatus(),
                requestParam.getStartDate(),
                requestParam.getEndDate(),
                userList
        );
        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream()
                    .filter(item -> Objects.equals(item.get("user").toString(), each.getUser()))
                    .findFirst()
                    .map(item -> item.get("uvType"))
                    .map(Object::toString)
                    .orElse("老访客");
            each.setUvType(uvType);
        });
        return actualResult;
    }

    @Override
    public ShortLinkStatsRespDTO groupShortLinkStats(ShortLinkGroupStatsReqDTO requestParams) {
        checkGroupBelongToUser(requestParams.getGid());
        List<LinkAccessStatsDO> linkAccessStatsDOS = linkAccessStatsMapper.listStatsByGroup(requestParams);
        if(CollUtil.isEmpty(linkAccessStatsDOS)) {
            return null;
        }
        // 基础访问数据
        LinkAccessStatsDO pvUvUidStatsByGroup = linkAccessStatsMapper.findPvUvUidStatsByGroup(requestParams);
        // 日常访问情况
        List<ShortLinkStatsAccessDailyRespDTO> daily = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(requestParams.getStartDate()), DateUtil.parse(requestParams.getEndDate()), DateField.DAY_OF_YEAR).stream()
                .map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> {
            linkAccessStatsDOS.stream()
                    .filter(item -> Objects.equals(each, DateUtil.formatDate(item.getDate())))
                    .findFirst()
                    .ifPresentOrElse(item -> {
                        ShortLinkStatsAccessDailyRespDTO dailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                                .date(each)
                                .pv(item.getPv())
                                .uv(item.getUv())
                                .uip(item.getUip())
                                .build();
                        daily.add(dailyRespDTO);
                    }, () -> {
                        ShortLinkStatsAccessDailyRespDTO dailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                                .uv(0)
                                .pv(0)
                                .uip(0)
                                .date(each)
                                .build();
                        daily.add(dailyRespDTO);
                    });
        });
        // 地区访问情况
        List<ShortLinkStatsLocaleCNRespDTO> localeCnStats = new ArrayList<>();
        List<LinkLocaleStatsDO> linkLocaleStatsDOS = linkLocaleStatsMapper.listLocaleByGroup(requestParams);
        int localeSum = linkLocaleStatsDOS.stream()
                .mapToInt(LinkLocaleStatsDO::getCnt)
                .sum();
        linkLocaleStatsDOS.forEach(each -> {
            double radio = (double) each.getCnt() / localeSum;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .locale(each.getProvince())
                    .cnt(each.getCnt())
                    .ratio(actualRadio)
                    .build();
            localeCnStats.add(localeCNRespDTO);
        });
        // 小时访问情况
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDO> listHourStatsByGroup = linkAccessStatsMapper.listHourStatsByGroup(requestParams);
        for(int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
            listHourStatsByGroup.stream()
                    .filter(item -> Objects.equals(item.getHour(), hour.get()))
                    .findFirst()
                    .ifPresentOrElse(item -> {
                        hourStats.add(item.getPv());
                    }, () -> hourStats.add(0));
        }
        // 高频ip统计
        List<ShortLinkStatsTopIpRespDTO> topIpStats = new ArrayList<>();
        List<HashMap<String, Object>> listTopIpByGroup = linkAccessLogsMapper.listTopIpByGroup(requestParams);
        listTopIpByGroup.forEach(each -> {
            ShortLinkStatsTopIpRespDTO statsTopIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpStats.add(statsTopIpRespDTO);
        });
        // 一周访问情况
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStatsDO> listWeekdayStatsByGroup = linkAccessStatsMapper.listWeekdayStatsByGroup(requestParams);
        for(int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByGroup.stream()
                    .filter(item -> Objects.equals(item.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStatsDO::getPv)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        // 浏览器访问情况
        List<ShortLinkStatsBrowserRespDTO> browserStats = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByGroup = linkBrowserStatsMapper.listBrowserStatsByGroup(requestParams);
        int browserSum = listBrowserStatsByGroup.stream()
                .mapToInt(item -> Integer.parseInt(item.get("count").toString()))
                .sum();
        listBrowserStatsByGroup.forEach(each -> {
            int browserCnt = Integer.parseInt(each.get("count").toString());
            double radio = (double) browserCnt / browserSum;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsBrowserRespDTO browserRespDTO = ShortLinkStatsBrowserRespDTO.builder()
                    .browser(each.get("browser").toString())
                    .cnt(browserCnt)
                    .ratio(actualRadio)
                    .build();
            browserStats.add(browserRespDTO);
        });
        // 操作系统访问情况
        List<ShortLinkStatsOsRespDTO> osStats = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByGroup = linkOsStatsMapper.listOsStatsByGroup(requestParams);
        int osSum = listOsStatsByGroup.stream()
                .mapToInt(item -> Integer.parseInt(item.get("count").toString()))
                .sum();
        listOsStatsByGroup.forEach(each -> {
            int osCnt = Integer.parseInt(each.get("count").toString());
            double radio = (double) osCnt / osSum;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsOsRespDTO osRespDo = ShortLinkStatsOsRespDTO.builder()
                    .os(each.get("os").toString())
                    .cnt(osCnt)
                    .ratio(actualRadio)
                    .build();
            osStats.add(osRespDo);
        });
        // 访问设备类型
        List<ShortLinkStatsDeviceRespDTO> deviceStats = new ArrayList<>();
        List<LinkDeviceStatsDO> linkDeviceStatsDOS = linkDeviceStatsMapper.listDeviceStatsByGroup(requestParams);
        int deviceSum = linkDeviceStatsDOS.stream()
                .mapToInt(LinkDeviceStatsDO::getCnt)
                .sum();
        linkDeviceStatsDOS.forEach(each -> {
            int deviceCnt = each.getCnt();
            double radio = (double) deviceCnt / deviceSum;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsDeviceRespDTO deviceRespDTO = ShortLinkStatsDeviceRespDTO.builder()
                    .device(each.getDevice())
                    .cnt(deviceCnt)
                    .ratio(actualRadio)
                    .build();
            deviceStats.add(deviceRespDTO);
        });
        // 访问网络类型
        List<ShortLinkStatsNetworkRespDTO> networkStats = new ArrayList<>();
        List<LinkNetworkStatsDO> linkNetworkStatsDOS = linkNetworkStatsMapper.listNetworkStatsByGroup(requestParams);
        int networkSum = linkNetworkStatsDOS.stream()
                .mapToInt(LinkNetworkStatsDO::getCnt)
                .sum();
        linkNetworkStatsDOS.forEach(each -> {
            int networkCnt = each.getCnt();
            double radio = (double) networkCnt / networkSum;
            double actualRadio = Math.round(radio * 100.0) / 100.0;
            ShortLinkStatsNetworkRespDTO networkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .network(each.getNetwork())
                    .cnt(networkCnt)
                    .ratio(actualRadio)
                    .build();
            networkStats.add(networkRespDTO);
        });
        return ShortLinkStatsRespDTO.builder()
                .pv(pvUvUidStatsByGroup.getPv())
                .uv(pvUvUidStatsByGroup.getUv())
                .uip(pvUvUidStatsByGroup.getUip())
                .daily(daily)
                .localeCnStats(localeCnStats)
                .hourStats(hourStats)
                .topIpStats(topIpStats)
                .weekdayStats(weekdayStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();
    }

    @Override
    public IPage<ShortLinkStatsAccessRecordRespDTO> groupShortLinkStatsAccessRecord(ShortLinkGroupStatsAccessRecordReqDTO requestParam) {
        checkGroupBelongToUser(requestParam.getGid());
        IPage<LinkAccessLogsDO> linkAccessLogsDOIPage = linkAccessLogsMapper.selectGroupPage(requestParam);
        if(CollUtil.isEmpty(linkAccessLogsDOIPage.getRecords())) {
            return new Page<>();
        }
        IPage<ShortLinkStatsAccessRecordRespDTO> actualResult = linkAccessLogsDOIPage.convert(
                each -> BeanUtil.toBean(each, ShortLinkStatsAccessRecordRespDTO.class)
        );
        List<String> userList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordRespDTO::getUser)
                .toList();
        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectGroupUvTypeByUsers(
                requestParam.getGid(),
                requestParam.getStartDate(),
                requestParam.getEndDate(),
                userList
        );
        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream()
                    .filter(item -> Objects.equals(item.get("user").toString(), each.getUser()))
                    .findFirst()
                    .map(item -> item.get("uvType").toString())
                    .orElse("老访客");
            each.setUvType(uvType);
        });
        return actualResult;
    }

    public void checkGroupBelongToUser(String gid) throws ServiceException {
        String username = Optional.ofNullable(UserContext.getUsername())
                .orElseThrow(() -> new ServiceException("用户未登录"));
        LambdaQueryWrapper<GroupDO> queryWrapper = new LambdaQueryWrapper<GroupDO>()
                .eq(GroupDO::getGid, gid)
                .eq(GroupDO::getUsername, username)
                .eq(GroupDO::getDelFlag, 0);
        List<GroupDO> groupDOS = linkGroupMapper.selectList(queryWrapper);
        if(CollUtil.isEmpty(groupDOS)) {
            throw new ServiceException("用户信息与分组标识不匹配");
        }
    }
}
